package com.lry.jvm;

import com.lry.jvm.classfile.ClassFile;
import com.lry.jvm.classfile.MemberInfo;
import com.lry.jvm.classfile.attributes.AttributeInfo;
import com.lry.jvm.classfile.constantpool.ConstantInfo;
import com.lry.jvm.classfile.constantpool.ConstantPool;
import com.lry.jvm.classpath.Classpath;
import com.lry.jvm.example.Anno;
import com.lry.jvm.rtda.Frame;
import com.lry.jvm.rtda.LocalVars;
import com.lry.jvm.rtda.OperandStack;
import com.lry.jvm.rtda.heap.JClass;
import com.lry.jvm.rtda.heap.JClassLoader;
import com.lry.jvm.rtda.heap.JMethod;
import com.lry.jvm.util.Util;

@Anno(value = "main value")
public class Main {

    public static void main(String[] argv) throws Exception {
        Args args = Args.parse(argv);
        if(!args.ok || args.helpFlag){
            System.out.println("Usage: <main class> [-options] class [args...]");
        }else if (args.versionFlag) {
            System.out.println("java version \"1.8.0\"");
        } else {
            startJVM(args);
        }
    }
    //-cp E:\lry\project\jvm\target\classes com.lry.jvm.example.exp1.Exp1
    private static void startJVM(Args args) throws Exception {
        Classpath cp = new Classpath(args.jre, args.classpath);
        //暂时只是打印一些信息
        System.out.printf("classpath:%s class:%s args:%s\n",
                args.classpath, args.getMainClass(), args.getAppArgs());
        String className = args.getMainClass().replace(".", "/");
        JClassLoader loader = new JClassLoader(cp);
        JClass jClass = loader.loadClass(className);

        printClassInfo(jClass.getCf());

        JMethod mainMethod = jClass.getMainMethod();
        if(null==mainMethod){
            Util.panic("no find mainMethod");
        }
        Interpreter.execute(mainMethod);
    }

    private static void test(){
        //测试局部变量表和操作数栈
        Frame frame = new Frame(100,100);

        //本地变量表
        testLocalVars(frame.getLocalVars());

        //操作数栈
        testOperandStack(frame.getOperandStack());
    }



    private static void testOperandStack(OperandStack ops) {
        ops.pushInt(100) ;
        ops.pushInt(-100) ;
        ops.pushLong(29979245801L) ;
        ops.pushLong(-29979245801L);
        ops.pushFloat(3.1415926f);
        ops.pushDouble(2.71828182845);
        ops.pushRef(null) ;

        System.out.println(ops.popRef()) ;
        System.out.println(ops.popDouble());
        System.out.println(ops.popFloat()) ;
        System.out.println(ops.popLong()) ;
        System.out.println(ops.popLong()) ;
        System.out.println(ops.popInt());
        System.out.println(ops.popInt());
    }

    private static void testLocalVars(LocalVars localVars) {
        localVars.setInt(0,100);
        localVars.setInt(1,-100);
        localVars.setLong(2,29979245809L);
        localVars.setLong(4,-29979245809L);
        localVars.setFloat(6, 3.1415926f);
        localVars.setDouble(7, 2.718281828324);
        localVars.setRef(9, null);
        System.out.println(localVars.getInt(0)) ;
        System.out.println(localVars.getInt(1)) ;
        System.out.println(localVars.getLong(2)) ;
        System.out.println(localVars.getLong(4));
        System.out.println(localVars.getFloat(6)) ;
        System.out.println(localVars.getDouble(7)) ;
        System.out.println(localVars.getRef(9));
    }


    private static void printClassInfo(ClassFile cf) {
        System.out.println("version: "+ cf.getMajorVersion()+"."+cf.getMinorVersion());

        ConstantPool constantPool = cf.getConstantPool();
        ConstantInfo[] constantInfos = constantPool.getConstantInfos();
        System.out.println("constants count: "+ constantPool.getConstantPoolSize());
        for(int i = 1; i < constantPool.getConstantPoolSize() ; i++) {

            if(constantInfos[i]!=null){
                System.out.println(i+": "+constantInfos[i]);
            }
        }


        System.out.format("access flags: 0x%x\n", cf.getAccessFlag());
        System.out.println("this class: "+ constantPool.getUTF8(cf.getClassNameIndex()));
        System.out.println("super class: "+ constantPool.getUTF8(cf.getSuperClassNameIndex()));
        System.out.println("interfaces: "+cf.getInterfaceIndexes().length);
        MemberInfo[] fields = cf.getFields();
        System.out.println("fields count: "+ fields.length);
        for (MemberInfo memberInfo : fields) {
            System.out.format("  %s\n", constantPool.getUTF8(memberInfo.getNameIndex()));
        }

        MemberInfo[] methods = cf.getMethods();
        System.out.println("methods count: "+ methods.length);
        for (MemberInfo memberInfo : methods) {
            System.out.format("  %s\n", constantPool.getUTF8(memberInfo.getNameIndex()));
        }

        AttributeInfo[] attributes = cf.getAttributes();
        System.out.println("attributes count: "+ attributes.length);
        for (AttributeInfo attributeInfo : attributes) {
            System.out.format("  %s\n", attributeInfo);
        }

    }

    @Anno("panic value")
    public static void panic(String msg) {
        System.out.println(msg);
        System.exit(0);
    }

    class A{}

}
